package com.xci.core.helper;

import cn.hutool.core.io.FileUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.introspect.Annotated;
import com.fasterxml.jackson.databind.introspect.AnnotatedMethod;
import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
import com.google.common.collect.Lists;
import com.xci.core.internal.Const;
import org.springframework.format.annotation.DateTimeFormat;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.AnnotatedElement;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;

/**
 * Json序列化/反序列化帮助类
 *
 * @author 吕艳阳
 */
public class JsonHelper {
    private final static ObjectMapper MAPPER;
    private static ObjectMapper mapper;

    static {
        SimpleDateFormat dateFormat = new SimpleDateFormat(Const.DEFAULT_DATETIME_PATTERN);

        MAPPER = new ObjectMapper();
        MAPPER.setDateFormat(dateFormat);
        MAPPER.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
            private static final long serialVersionUID = -5854941510519564900L;

            @Override
            public Object findSerializer(Annotated a) {
                if (a instanceof AnnotatedMethod) {
                    AnnotatedElement m = a.getAnnotated();
                    DateTimeFormat an = m.getAnnotation(DateTimeFormat.class);
                    if (an != null) {
                        if (!Const.DEFAULT_DATETIME_PATTERN.equals(an.pattern())) {
                            return new JsonDateSerializer(an.pattern());
                        }
                    }
                }
                return super.findSerializer(a);
            }
        });
    }

    private static class JsonDateSerializer extends JsonSerializer<Date> {
        private String format;

        private JsonDateSerializer(String format) {
            this.format = format;
        }

        @Override
        public void serialize(Date date, JsonGenerator gen, SerializerProvider provider) throws IOException {
            SimpleDateFormat dateFormat = new SimpleDateFormat(format);
            String value = dateFormat.format(date);
            gen.writeString(value);
        }
    }

    private static ObjectMapper getMapper() {
        if (mapper == null) {
            mapper = SpringHelper.getBean(ObjectMapper.class);
        }
        return mapper == null ? MAPPER : mapper;
    }

    /**
     * 对象转Json字符串
     */
    public static String serialize(Object object) {
        return serialize(object, false);
    }

    /**
     * 对象转Json字符串
     *
     * @param isPretty 是否格式化保存
     */
    public static String serialize(Object object, boolean isPretty) {
        if (object == null) {
            return "";
        }
        String jsonString;
        try {
            var mapper = getMapper();
            if (isPretty) {
                jsonString = mapper.writerWithDefaultPrettyPrinter().writeValueAsString(object);
            } else {
                jsonString = mapper.writeValueAsString(object);
            }
        } catch (JsonProcessingException e) {
            throw new RuntimeException("转换json字符失败!", e);
        }
        return jsonString;
    }

    /**
     * Json字符串转对象
     */
    public static <T> T deserialize(String data, Class<T> c) {
        try {
            return getMapper().readValue(data, c);
        } catch (IOException e) {
            throw new RuntimeException("将json字符转换为对象时失败!", e);
        }
    }

    /**
     * Json字符串转对象
     */
    public static <T> T deserialize(String data, JavaType valueType) {
        try {
            return getMapper().readValue(data, valueType);
        } catch (IOException e) {
            throw new RuntimeException("将json字符转换为对象时失败!", e);
        }
    }

    /**
     * Json字符串转对象
     */
    public static <T> T deserialize(String data, Class<?> collectionClass, Class<?> elementClass) {
        try {
            return getMapper().readValue(data, getCollectionType(collectionClass, elementClass));
        } catch (IOException e) {
            throw new RuntimeException("将json字符转换为对象时失败!", e);
        }
    }

    /**
     * Json字符串转对象
     */
    public static <T> T deserializeMap(String data, Class<?> collectionClass, Class<?> keyClass, Class<?> elementClass) {
        try {
            return getMapper().readValue(data, getCollectionType(collectionClass, keyClass, elementClass));
        } catch (IOException e) {
            throw new RuntimeException("将json字符转换为对象时失败!", e);
        }
    }

    private static JavaType getCollectionType(Class<?> collectionClass, Class<?>... elementClasses) {
        return getMapper().getTypeFactory().constructParametricType(collectionClass, elementClasses);
    }

    /**
     * 加载 json 文件数据
     *
     * @param rootPath        项目根路径
     * @param collectionClass 集合类型
     * @param elementClass    元素类型
     * @param <T>             对象类型
     * @return 数据集合
     */
    public static <T> List<T> loadList(String rootPath, Class<?> collectionClass, Class<?> elementClass) {
        File file = FileHelper.buildRootFile(rootPath);
        return loadList(file, collectionClass, elementClass);
    }

    /**
     * 加载 json 文件数据
     *
     * @param file            文件对象
     * @param collectionClass 集合类型
     * @param elementClass    元素类型
     * @param <T>             对象类型
     * @return 数据集合
     */
    public static <T> List<T> loadList(File file, Class<?> collectionClass, Class<?> elementClass) {
        if (file.exists()) {
            String content = FileUtil.readUtf8String(file);
            if (Helper.isNotBlank(content)) {
                return deserialize(content, collectionClass, elementClass);
            }
        }
        return Lists.newArrayList();
    }

    /**
     * 保存 json 文件数据
     *
     * @param rootPath 项目根路径
     * @param list     数据列表
     */
    public static void saveList(String rootPath, List list) {
        File file = FileHelper.buildRootFile(rootPath);
        saveList(file, list);
    }

    /**
     * 保存 json 文件数据
     *
     * @param file 文件对象
     * @param list 数据列表
     */
    public static void saveList(File file, List list) {
        String content = serialize(list);
        FileUtil.mkParentDirs(file);
        FileUtil.writeUtf8String(content, file);
    }
}
